﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Chromos.ActionEngine;
using Chromos.AIEngine;

namespace Chromos.Actors
{
	public enum FACING
	{
		LEFT,
		RIGHT
	}

	public abstract class Actor
	{
		public Game1 game;
		public Vector2 position, velocity, acceleration, size;
		public int mass;
		protected ContentManager content;
		protected float fallAccelerationFactor = 1.0f;
		protected Texture2D currentTexture;
		protected ActorActionEngine actionEngine;
		protected ActorAIEngine aiEngine;
		public bool isStatic;
		public bool applyGravity = true;
		public RectangleF boundingBox;
		protected Vector2 boundingInflate = new Vector2(0, 0);
		private bool loaded = false;
		private FACING facing = FACING.LEFT;
		public bool hasGrounded = false;

		// Game fields
		public int health;

		//private delegate Projectile ProjectileGenerator(Game1 game, Vector2 position, Vector2 velocity, ContentManager content, bool isStatic, float seekPercentage, List<Actor> targets);
		
		public Actor(Game1 game, Vector2 position, Vector2 velocity, ContentManager content, bool isStatic)
		{
			this.game = game;
			this.position = position;
			this.velocity = velocity;
			this.content = content;
			this.isStatic = isStatic;
			this.size = new Vector2(30, 30);
			position = new Vector2();
			velocity = new Vector2();
			acceleration = new Vector2();
			actionEngine = new ActorActionEngine(this);
			initActorFields();
			addActions();
			initAIEngine();
			updateBoundingBox(false);
		}

		/// <summary>
		/// Generates projectiles based on the int given (which is the index of the attack called)
		/// </summary>
		/// <param name="i"></param>
		/// <returns></returns>
		public abstract Projectile createProjectiles(int i);

		/// <summary>
		/// Inits all fields such as health that were not passed in on creation
		/// </summary>
		protected abstract void initActorFields();

		/// <summary>
		/// Moves the bounding box, and the actor along with it. For physics
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		public void moveBounding(float x, float y)
		{
			this.position.X += x;
			this.position.Y += y;
			updateBoundingBox(false);
		}
		/// <summary>
		/// Sets the position of the bounding box and actor. For physics
		/// </summary>
		/// <param name="?"></param>
		public void setBounding(float x, float y)
		{
			moveBounding(x - boundingBox.X, y - boundingBox.Y);
		}

		protected void updateBoundingBox(bool recreate)
		{
			if (this.boundingBox == null)
			{
				this.boundingBox = new RectangleF(position.X, position.Y, size.X, size.Y);
				this.boundingBox.Inflate(boundingInflate.X, boundingInflate.Y);
			}
			else
			{
				if (!recreate)
				{
					this.boundingBox.X = (this.position.X - boundingInflate.X);
					this.boundingBox.Y = (this.position.Y - boundingInflate.Y);
				}
				else
				{
					this.boundingBox = new RectangleF(position.X, position.Y, size.X, size.Y);
					this.boundingBox.Inflate(boundingInflate.X, boundingInflate.Y);
				}
				//this.boundingBox.Width = size.X - 2 * boundingInflate.X;
				//this.boundingBox.Width = size.Y - 2 * boundingInflate.Y;
			}
		}

		protected abstract void initAIEngine();

		/// <summary>
		/// Call this to load the object, add the animations, etc
		/// Set current animation to something
		/// </summary>
		public void load()
		{
			actionEngine.load();
			update();
			loaded = true;
		}
		public bool isLoaded()
		{
			return loaded;
		}

		/// <summary>
		/// Called by load. Should load the images into animations 
		/// and add them to their respective lists.
		/// </summary>
		public abstract void addActions();

		public void update()
		{
			aiEngine.update();
			actionEngine.update();
			this.currentTexture = actionEngine.getCurrentImage();
			updateBoundingBox(false);
			if (this.health <= 0)
				aiEngine.kill();
		}

		public ActorAIEngine getAIEngine()
		{
			return aiEngine;
		}
		public ActorActionEngine getActionEngine()
		{
			return actionEngine;
		}
		public Texture2D getCurrentImage()
		{
			return this.currentTexture;
		}

		/// <summary>
		/// This should only be used in special circumstances
		/// </summary>
		/// <param name="index"></param>
		public void setCurrentAnimation(int index)
		{
			//currentTexture = movements.ElementAt(0).
		}

		public bool getDead()
		{
			return aiEngine.getDead();
		}
		public void setAlive()
		{
			aiEngine.setIdle();
		}

		public abstract void collidedProjectile(Projectile p);

		public void setFacing(FACING facing)
		{
			this.facing = facing;
		}

		public FACING getFacing()
		{
			return this.facing;
		}
	}
}
